package org.raymond.icloud.common.starter.webFilter;

import cn.hutool.core.util.CharsetUtil;
import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.crypto.asymmetric.RSA;
import cn.hutool.crypto.symmetric.AES;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.assertj.core.util.Sets;
import org.raymond.icloud.common.core.constants.CommonConstants;
import org.raymond.icloud.common.core.constants.ErrorCode;
import org.raymond.icloud.common.core.exception.BaseException;
import org.raymond.icloud.common.core.util.ApplicationContextUtils;
import org.raymond.icloud.common.core.util.RedisUtil;
import org.raymond.icloud.common.core.util.http.CachedDecryptRequestWrapper;
import org.raymond.icloud.common.core.util.http.CachedEncryptResponseWrapper;
import org.springframework.context.ApplicationContext;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.servlet.HandlerExceptionResolver;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Set;

@Slf4j
public class SecurityFilter extends AbstractFilter {
    private static final Set<String> allowedUriList = Sets.newLinkedHashSet(
            "/icloud/swagger-ui/**/*", "/icloud/v3/api-docs",
            "/icloud/client/**/*", "/icloud/common/**/*", "/icloud/admin/**/*"
    );

    private HandlerExceptionResolver resolver;
    private RSA rsa;
    private RedisUtil redisUtil;

    @Override
    protected void initFilterBean() throws ServletException {
        ApplicationContext ctx = ApplicationContextUtils.getApplicationContext();
        resolver = ctx.getBean("handlerExceptionResolver", HandlerExceptionResolver.class);
        rsa = ctx.getBean("rsa", RSA.class);
        redisUtil = ctx.getBean("redisUtil", RedisUtil.class);
    }

    @Override
    protected void doFilterSub(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        String requestURI = request.getRequestURI();
        AntPathMatcher uriMatcher = new AntPathMatcher();
        if(!allowedUriList.stream().anyMatch(e->uriMatcher.match(e, requestURI))){
            resolver.resolveException(request, response, null, new BaseException(ErrorCode.NOT_FOUND, "URI not found"));
            return;
        }

        String encryptedKey = request.getHeader(CommonConstants.SYMMETRY_HEADER_CRYPTO);
        String encryptedIv = request.getHeader(CommonConstants.SYMMETRY_HEADER_IV);
        if(StringUtils.isBlank(encryptedKey) || StringUtils.isBlank(encryptedIv)){
            log.error("Key or iv is empty. key:{}, iv:{}", encryptedKey, encryptedIv);
            resolver.resolveException(request, response, null, new BaseException(ErrorCode.BAD_REQUEST));
            return;
        }
        String key = rsa.decryptStr(encryptedKey, KeyType.PrivateKey, CharsetUtil.CHARSET_UTF_8);
        String iv = rsa.decryptStr(encryptedIv, KeyType.PrivateKey, CharsetUtil.CHARSET_UTF_8);
        AES aes = new AES("CBC", "PKCS7Padding", key.getBytes(StandardCharsets.UTF_8), iv.getBytes(StandardCharsets.UTF_8));
        log.info("aes key: {}", key);

        CachedDecryptRequestWrapper cachedRequestWrapper = new CachedDecryptRequestWrapper(request, aes);
        CachedEncryptResponseWrapper cachedResponseWrapper = new CachedEncryptResponseWrapper(response, aes);
        try {
            filterChain.doFilter(cachedRequestWrapper, cachedResponseWrapper);
        } finally {
            cachedResponseWrapper.updateContent();
        }
    }
}
